home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_oth / forchek1 / forlex.c < prev    next >
C/C++ Source or Header  |  1991-11-06  |  44KB  |  1,773 lines

  1. /* forlex.c:
  2.  
  3.  Tokenizing routines for Fortran program checker.
  4.  
  5.     Copyright (C) 1991 by Robert K. Moniot.
  6.     This program is free software.  Permission is granted to
  7.     modify it and/or redistribute it, retaining this notice.
  8.     No guarantees accompany this software.
  9.  
  10.  
  11. Contains three previously independent modules:
  12.    I. Forlex  -- yylex function which gives tokens to the parser, and
  13.       related functions.
  14.   II. Advance -- bottom-level scanning of input stream.
  15.  III. Keywords -- disambiguates keywords from identifiers.
  16.  
  17.  Scan ahead for the label I. II. or III. to find desired module.
  18. */
  19.  
  20.  
  21.  
  22.  /* Declarations shared by all modules */
  23.  
  24. #include <stdio.h>
  25. #include <ctype.h>
  26. #include <string.h>
  27. #ifdef __STDC__
  28. #include <stdlib.h>
  29. #else
  30. char *calloc();
  31. #endif
  32.  
  33. #include "forchek.h"
  34. #include "tokdefs.h"
  35. #include "symtab.h"
  36.  
  37. /* lexdefs.h:
  38.   Macros and shared info for lexical analysis routines
  39. */
  40.  
  41.  
  42. #define EOL     '\n'    /* Character for end of line, not of statement */
  43.  
  44. extern YYSTYPE yylval;   /* Lexical value for Yacc */
  45.  
  46.  
  47.  /* Since EOS is special, need special macros for it */
  48. #define makeupper(C) (((C) != EOS && islower((int)(C)))? toupper((int)(C)):(C))
  49. #define iswhitespace(C) ( (C) != EOS && isspace((int)(C)) )
  50. #define isadigit(C)     ( (C) != EOS && isdigit((int)(C)) )
  51. #define isaletter(C)    ( (C) != EOS && isalpha((int)(C)) )
  52.  
  53.  /* define isidletter to allow underscore and/or dollar sign or not */
  54. #if ALLOW_UNDERSCORES && ALLOW_DOLLARSIGNS
  55.     /* both underscore and dollar sign */
  56. #define isidletter(C)    ( (C) != EOS && (isalpha((int)(C)) || \
  57.        (C) == '_' || (C) == '$') )
  58. #else
  59. #if ALLOW_UNDERSCORES  /* underscore and not dollar sign */
  60. #define isidletter(C)    ( (C) != EOS && (isalpha((int)(C))||(C) == '_') )
  61. #else
  62. #if ALLOW_DOLLARSIGNS  /* dollar sign and not underscore */
  63. #define isidletter(C)    ( (C) != EOS && (isalpha((int)(C))||(C) == '$') )
  64. #else    /* neither dollar sign nor underscore */
  65. #define isidletter(C)    isaletter(C)
  66. #endif
  67. #endif
  68. #endif
  69.  
  70.  
  71. int
  72.  inside_quotes,  /* TRUE when reading a string */
  73.  curr_char,  /* Current input character */
  74.  next_char;  /* Lookahead character */
  75.  
  76. extern int complex_const_allowed,    /* shared flags operated by fortran.y */
  77.     inside_format,
  78.     integer_context;
  79. extern int stmt_sequence_no; /* shared with fortran.y */
  80.  
  81.   /* Declare shared lexical routines */
  82. void advance();
  83. int is_keyword(), looking_at();
  84.  
  85.  
  86.  
  87.  
  88. /*
  89.  
  90. I. Forlex
  91.  
  92.    Shared functions defined:
  93.  yylex()   Returns next token.  Called from yyparse().
  94.  implied_id_token(t,s) Creates token for blank common declaration.
  95.  
  96. Note: compilation options LEX_STORE_STRINGS and LEX_STORE_HOLLERITHS:
  97.   Define the macro name LEX_STORE_STRINGS to build a version of forchek that
  98.   stores string constants, and LEX_STORE_HOLLERITHS to store hollerith
  99.   constants.  Now that INCLUDE statements are supported, strings must
  100.   be stored.  Holleriths are not used, so they need not be stored.
  101. */
  102. #define LEX_STORE_STRINGS
  103.  
  104. #include <math.h>
  105.  
  106.  
  107.  
  108.  /* The following macro says whether a given character is legal,
  109.   * i.e. one of the stream control chars or a valid ANSI Fortran
  110.   * character.  Lower case letters are considered legal too.
  111.   * Nondigits in columns 1-6 (except EOF,EOS) are illegal
  112.   */
  113. #define islegal(C) ( ((C) == EOF) || ((C) == EOS) || \
  114.  ( (col_num >= 6 || isdigit(C)) && \
  115.   ((C) >= ' ' && (C) <= 'z' && legal_chars[(C)-' '] == (C))) )
  116.  
  117.   /* Array has x where ASCII character is not valid */
  118. PRIVATE char legal_chars[]=
  119. #ifdef ALLOW_UNDERSCORES
  120. " xxx$xx'()*+,-./0123456789:xx=xxx\
  121. ABCDEFGHIJKLMNOPQRSTUVWXYZxxxx_xabcdefghijklmnopqrstuvwxyz";
  122. #else
  123. " xxx$xx'()*+,-./0123456789:xx=xxx\
  124. ABCDEFGHIJKLMNOPQRSTUVWXYZxxxxxxabcdefghijklmnopqrstuvwxyz";
  125. #endif
  126.  
  127.   /* local functions defined */
  128. PRIVATE void
  129.  get_dotted_keyword(), get_hollerith(),
  130.  get_identifier(), get_illegal_token(), get_label(),
  131.  get_letter(), get_number(), get_punctuation(), get_string(),
  132.  get_complex_const();
  133.  
  134.  
  135.  
  136.  
  137.   /*  Gets next token for Yacc.  Return value is token.class,
  138.    *  and a copy of the token is stored in yylval.
  139.    */
  140. int
  141. yylex()
  142. {
  143.     Token token;
  144.  
  145.   /* Initialize token fields to scratch. */
  146.     token.subclass = 0;
  147.     token.value.integer = 0;
  148.  
  149.     if(curr_char == EOF) {
  150.  token.class = EOF;
  151.  token.line_num = line_num;
  152.  token.col_num = col_num;
  153.     }
  154.     else {
  155.  
  156.   /* Skip leading spaces, and give error message if non-ANSI
  157.    * characters are found.
  158.    */
  159.  
  160.  while(iswhitespace(curr_char) || (! islegal(curr_char))  ) {
  161.    if(!iswhitespace(curr_char))
  162.   yyerror("Illegal character");
  163.    advance();
  164.  }
  165.  
  166.  token.line_num = line_num;
  167.  token.col_num = col_num;
  168.  
  169.  if(isadigit(curr_char)) {
  170.   if(col_num < 6)
  171.    get_label(&token);      /* Stmt label */
  172.   else
  173.    get_number(&token);     /* Numeric or hollerith const */
  174.  }
  175.  else if(isaletter(curr_char)) {
  176.   if(implicit_letter_flag)
  177.    get_letter(&token); /* letter in IMPLICIT list */
  178.   else
  179.    get_identifier(&token); /* Identifier or keyword */
  180.  }
  181.  else {
  182.     switch(curr_char) {
  183. #ifdef ALLOW_UNDERSCORES
  184.       case '_': get_identifier(&token); /* Identifier with initial _ */
  185.         break;
  186. #endif
  187.       case  '.':
  188.   if(isadigit(next_char))
  189.    get_number(&token);     /* Numeric const */
  190.   else if(isaletter(next_char))
  191.    get_dotted_keyword(&token);     /* .EQ. etc. */
  192.   else {
  193.    get_punctuation(&token); /* "." out of place */
  194.   }
  195.   break;
  196.  
  197.       case '\'':
  198.    get_string(&token);     /* Quoted string */
  199.   break;
  200.  
  201.  
  202.       default:
  203.    get_punctuation(&token);  /* Punctuation character */
  204.   break;
  205.     }
  206.  }
  207.     }
  208.  
  209.     if(token.class == EOS) {
  210.  implicit_flag=FALSE; /* in case of errors, reset flags */
  211.  implicit_letter_flag = FALSE;
  212.     }
  213.  
  214.  
  215.     prev_token_class = token.class;
  216.  
  217.     yylval = token;
  218.     return token.class;
  219.  
  220. } /* yylex */
  221.  
  222.  
  223.  
  224.  /* Fills argument with token for an identifer, as if an identifer
  225.   * with name given by string s had been lexed.  This will
  226.   * be called by parser when blank common declaration is seen,
  227.   * and when a main prog without program statement is found
  228.   * so processing of named and unnamed cases can be handled uniformly.
  229.  */
  230. void
  231. implied_id_token(t,s)
  232.  Token *t;
  233.  char *s;
  234. {
  235.  int h;
  236.  unsigned long hnum;
  237.  
  238.  hnum = hash(s);
  239.  while( h=hnum%HASHSZ, hashtab[h].name != NULL &&
  240.   strcmp(hashtab[h].name,s) != 0)
  241.    hnum = rehash(hnum);
  242.  if(hashtab[h].name == NULL) { /* not seen before */
  243.   hashtab[h].name = s;
  244.   hashtab[h].loc_symtab = NULL;
  245.   hashtab[h].glob_symtab = NULL;
  246.   hashtab[h].com_loc_symtab = NULL;
  247.   hashtab[h].com_glob_symtab = NULL;
  248.  }
  249.  t->class = tok_identifier;
  250.  t->value.integer = h;
  251.  
  252. } /* implied_id_token */
  253.  
  254.  
  255.  
  256. struct {
  257.  char *name;
  258.  int class,subclass;
  259.  } dotted_keywords[]={   {"FALSE",tok_logical_const,FALSE},
  260.    {"TRUE",tok_logical_const,TRUE},
  261.    {"EQ",tok_relop,relop_EQ},
  262.    {"NE",tok_relop,relop_NE},
  263.    {"LE",tok_relop,relop_LE},
  264.    {"LT",tok_relop,relop_LT},
  265.    {"GE",tok_relop,relop_GE},
  266.    {"GT",tok_relop,relop_GT},
  267.    {"AND",tok_AND,0},
  268.    {"OR",tok_OR,0},
  269.    {"EQV",tok_EQV,0},
  270.    {"NEQV",tok_NEQV,0},
  271.    {"NOT",tok_NOT,0},
  272.    {NULL,0,0}
  273.       };
  274.  
  275.  
  276. PRIVATE void
  277. get_dotted_keyword(token)
  278.  Token *token;
  279. {
  280.  char s[8];
  281.  int i=0;
  282.  
  283.  initial_flag = FALSE;
  284.  
  285.  advance();      /* gobble the initial '.' */
  286.  while(isaletter(curr_char)) {
  287.     if(i < 7)
  288.   s[i++] = makeupper(curr_char);
  289.     advance();
  290.  }
  291.  s[i] = '\0';
  292.  
  293.  if(curr_char != '.') {
  294.      yyerror("Badly formed logical/relational operator or constant");
  295.  }
  296.  else {
  297.   advance();      /* gobble the final '.' */
  298.  }
  299.  
  300.  for(i=0; dotted_keywords[i].name != NULL; i++) {
  301.   if(strcmp(s,dotted_keywords[i].name) == 0) {
  302.    token->class = dotted_keywords[i].class;
  303.    token->subclass = dotted_keywords[i].subclass;
  304.    token->value.string = dotted_keywords[i].name;
  305.    if(debug_lexer)
  306.       fprintf(list_fd,"\nDotted keyword:\t\t%s",
  307.          dotted_keywords[i].name);
  308.    return;
  309.   }
  310.  }
  311.    /* Match not found: signal an error */
  312.  yyerror("Unknown logical/relational operator or constant");
  313.  get_illegal_token(token);
  314.  
  315. } /* get_dotted_keyword */
  316.  
  317.  
  318. PRIVATE void
  319. get_hollerith(token,n)  /* Gets string of form nHaaaa */
  320.  Token *token;
  321.  int n;
  322. {
  323.  int i;
  324. /* Holl. consts are not stored unless the macro name LEX_STORE_HOLLERITHS
  325.    is defined. */
  326. #ifdef LEX_STORE_HOLLERITHS
  327.  char *s;
  328. #else
  329.         char *s = "Not stored";
  330. #endif
  331.  initial_flag = FALSE;
  332.  inside_quotes = TRUE;
  333.  
  334. #ifdef LEX_STORE_HOLLERITHS
  335.  if( (s=(char *)calloc((unsigned)(n+1),1)) == (char *)NULL ) {
  336.   fprintf(stderr,"Out of string space at line %u\n",line_num);
  337.   for(i=0; i<n; i++)
  338.    advance();
  339.  }
  340.  else
  341. #endif
  342.  {
  343.   for(i=0; i<n; i++) {
  344.    while(curr_char == EOL) /* Skip over end of line */
  345.     advance();
  346.    if(curr_char == EOS || curr_char == EOF) {
  347.     n = i;   /* Premature end of string */
  348.    }
  349.    else
  350.    {
  351. #ifdef LEX_STORE_HOLLERITHS
  352.     s[i] = curr_char;
  353. #endif
  354.     advance();
  355.    }
  356.   }
  357. #ifdef LEX_STORE_HOLLERITHS
  358.   s[n] = '\0';
  359. #endif
  360.  }
  361.  
  362.  
  363.  inside_quotes = FALSE;
  364.  token->class = tok_hollerith;
  365.  token->value.string = s;
  366.  if(debug_lexer)
  367.   fprintf(list_fd,"\nHollerith:\t\t%s",s);
  368.  
  369. } /* get_hollerith */
  370.  
  371.  
  372. PRIVATE void
  373. get_identifier(token)
  374.  Token *token;
  375. {
  376.  char s[MAXIDSIZE+1];
  377.  int i=0;
  378.  
  379.    /* This loop gets  letter [letter|digit]* forms */
  380.  while(isidletter(curr_char) || isadigit(curr_char)) {
  381.   if(i < MAXIDSIZE)
  382.    s[i++] = makeupper(curr_char);
  383.   advance();
  384.  }
  385.  
  386.    /* If followed by .number then it may be a FORMAT edit
  387.       descriptor. */
  388.  
  389.  if(inside_format && curr_char == '.' && isadigit(next_char) ) {
  390.   if(i < MAXIDSIZE)
  391.    s[i++] = curr_char; /* store the '.' */
  392.   advance();
  393.   while( isadigit(curr_char) ) {
  394.    if(i < MAXIDSIZE)
  395.     s[i++] = curr_char;
  396.    advance();
  397.   }
  398.   token->class = tok_edit_descriptor;
  399.  }
  400.  else
  401.   token->class = tok_identifier;
  402.  
  403.  s[i++] = '\0';
  404.  
  405.     if(token->class == tok_edit_descriptor) {
  406.             token->value.string = NULL;
  407.  }
  408.  else {  /* it is an identifier or keyword */
  409.          int keywd_class;
  410.  
  411.   if( (keywd_class = is_keyword(s)) != 0) {
  412.        token->class = keywd_class; /* It's a keyword */
  413.        token->value.string = NULL;
  414.   }
  415.                 else {
  416.     /* Identifier: find its hashtable entry or
  417.        create a new entry. */
  418.       token->value.integer = hash_lookup(s);
  419.   }
  420.  }
  421.  
  422.  if(debug_lexer){
  423.      switch(token->class) {
  424.   case tok_edit_descriptor:
  425.    fprintf(list_fd,"\nEdit descriptor:\t%s",s);
  426.    break;
  427.   case tok_identifier:
  428.    fprintf(list_fd,"\nIdentifier:\t\t%s",s);
  429.    break;
  430.   default:
  431.    fprintf(list_fd,"\nKeyword:\t\ttok_%s",s);
  432.    break;
  433.      }
  434.  }
  435. } /* get_identifier */
  436.  
  437.  
  438. PRIVATE void
  439. get_illegal_token(token) /* Handle an illegal input situation */
  440.  Token *token;
  441. {
  442.  token->class = tok_illegal;
  443.  if(debug_lexer)
  444.       fprintf(list_fd,"\nILLEGAL TOKEN");
  445.  
  446. } /* get_illegal_token */
  447.  
  448.  
  449.  
  450.   /* Read a label from label field. */
  451. PRIVATE void
  452. get_label(token)
  453.  Token *token;
  454. {
  455.  int value=0;
  456.  while( isadigit(curr_char) && col_num < 6 ) {
  457.   value = value*10 + (curr_char-'0');
  458.   advance();
  459.  }
  460.  token->class = tok_label;
  461.  token->subclass = value;
  462.  if(debug_lexer)
  463.   fprintf(list_fd,"\nLabel:\t\t\t%d",value);
  464.  
  465. } /* get_label */
  466.  
  467.  
  468. PRIVATE void
  469. get_letter(token)  /* Gets letter in IMPLICIT list */
  470.  Token *token;
  471. {
  472.  token->class = tok_letter;
  473.  token->subclass = makeupper(curr_char);
  474.  
  475.     if(debug_lexer)
  476.  fprintf(list_fd,"\nLetter:\t\t\t%c",token->subclass);
  477.  
  478.  advance();
  479.  
  480. } /* get_letter */
  481.  
  482.  
  483.  /* get_number reads a number and determines data type: integer,
  484.   * real, or double precision.
  485.   */
  486.  
  487. #ifdef BLANKS_IN_NUMBERS  /* tolerate blanks within numbers */
  488. #define SKIP_SP while(iswhitespace(curr_char)) advance()
  489. #else
  490. #define SKIP_SP
  491. #endif
  492.  
  493.  
  494. PRIVATE void
  495. get_number(token)
  496.  Token *token;
  497. {
  498.  double dvalue,leftside,rightside,pwr_of_ten;
  499.  int exponent,expsign,datatype,c,digit_seen=FALSE;
  500.  
  501.  initial_flag = FALSE;
  502.  
  503.  leftside = 0.0;
  504.  datatype = tok_integer_const;
  505.  while(isadigit(curr_char)) {
  506.   leftside = leftside*10.0 + (double)(curr_char-'0');
  507.   advance();
  508.   SKIP_SP;
  509.   digit_seen = TRUE;
  510.  }
  511.  
  512.   /* If context specifies integer expected, skip to end.
  513.      Otherwise scan on ahead for more. */
  514.     if( integer_context) {
  515.         if(!digit_seen) {
  516.      yyerror("integer expected");
  517.      advance(); /* gobble something to avoid infinite loop */
  518.  }
  519.     }
  520.     else {/* not integer_context */
  521.  if( makeupper(curr_char) == 'H' ){      /* nnH means hollerith */
  522.   advance();
  523.   if(leftside == 0.0) {
  524.    yyerror("Zero-length hollerith constant");
  525.    get_illegal_token(token);
  526.   }
  527.   else {
  528.    get_hollerith(token, (int)leftside);
  529.   }
  530.   return;
  531.  }
  532.  
  533.  rightside = 0.0;
  534.  pwr_of_ten = 1.0;
  535.  if( curr_char == '.' &&
  536.     ! looking_at(tok_relop) ) { /* don't be fooled by 1.eq.N */
  537.   datatype = tok_real_const;
  538.   advance();
  539.   SKIP_SP;
  540.   while(isadigit(curr_char)) {
  541.    rightside = rightside*10.0 + (double)(curr_char-'0');
  542.    pwr_of_ten *= 0.10;
  543.    advance();
  544.    SKIP_SP;
  545.   }
  546.  }
  547. if(debug_lexer)
  548.  dvalue = leftside + rightside*pwr_of_ten;
  549.  
  550.  exponent = 0;
  551.  expsign = 1;
  552.  
  553. #if 0/* old version */
  554.   /* If we now see E or D, it is a real/d.p. constant, unless
  555.      the E or D is followed by w.d which gives an edit descr */
  556.  if( ( (c = makeupper(curr_char)) == 'E' || c == 'D' )
  557.   && !( datatype==tok_integer_const && looking_at(tok_edit_descriptor)))
  558. #else/* new version */
  559.   /* Integer followed by E or D gives a real/d.p constant
  560.      unless we are inside a format statement, in which
  561.      case we have an edit descriptor. */
  562.  if( ( (c = makeupper(curr_char)) == 'E' || c == 'D' )
  563.   && !( datatype==tok_integer_const && inside_format) )
  564. #endif
  565.  {
  566.   datatype = ((c == 'E')? tok_real_const: tok_dp_const);
  567.   advance();
  568.   if(curr_char == '+') {
  569.    expsign = 1;
  570.    advance();
  571.   }
  572.   else if(curr_char == '-') {
  573.    expsign = -1;
  574.    advance();
  575.   }
  576.   if(!isadigit(curr_char)) {
  577.    yyerror("Badly formed real constant");
  578.   }
  579.   else while(isadigit(curr_char)) {
  580.    exponent = exponent*10 + (curr_char-'0');
  581.    advance();
  582.   }
  583.  
  584.  /*  Compute real value only if debugging. If it exceeds max magnitude,
  585.      computing it may cause crash. At this time, value of real const
  586.      is not used for anything. */
  587. if(debug_lexer)
  588.     dvalue *= pow(10.0, (double)(exponent*expsign));
  589. else
  590.     dvalue = 0.0;
  591.  
  592.  }
  593.     }/* end if(!integer_context) */
  594.  token->class = datatype;
  595.  switch(datatype) {
  596.     case tok_integer_const:
  597.   token->value.integer = (int)leftside;
  598. if(debug_lexer)
  599. fprintf(list_fd,"\nInteger const:\t\t%d",token->value.integer);
  600.   break;
  601.     case tok_real_const:
  602.    /* store single as double lest it overflow */
  603.   token->value.dbl = dvalue;
  604. if(debug_lexer)
  605. fprintf(list_fd,"\nReal const:\t\t%g",token->value.dbl);
  606.   break;
  607.     case tok_dp_const:
  608.   token->value.dbl = dvalue;
  609. if(debug_lexer)
  610. fprintf(list_fd,"\nDouble const:\t\t%lg",token->value.dbl);
  611.   break;
  612.  }
  613.  
  614. } /* get_number */
  615.  
  616.      /* get_complex_constant reads an entity of the form (num,num)
  617.       where num is any [signed] numeric constant.  It will only be
  618.       called when looking_at() has guaranteed that there is one there.
  619.       The token receives the real part as a number.  The imaginary part
  620.       is not stored.  Whitespace is allowed between ( and num, around
  621.       the comma, and between num and ) but not within num. */
  622.  
  623. PRIVATE void
  624. get_complex_const(token)
  625.  Token *token;
  626. {
  627.  Token imag_part; /* temporary to hold imag part */
  628.  double sign=1.0;
  629.  
  630.  initial_flag = FALSE;
  631.  
  632.  advance();  /* skip over the initial paren */
  633.  
  634.  while(iswhitespace(curr_char))
  635.    advance();
  636.  if(curr_char == '+' || curr_char == '-') {
  637.    if(curr_char == '-') sign = -1.0;
  638.    advance();
  639.    SKIP_SP;
  640.  }
  641.  
  642. if(debug_lexer){
  643. fprintf(list_fd,"\nComplex const:(");
  644. if(sign < 0.0) fprintf(list_fd," -");
  645. }
  646.  get_number(token);
  647.  switch(token->class) {
  648.     case tok_integer_const:
  649.   token->value.dbl = sign*(double)token->value.integer;
  650.   break;
  651.     case tok_real_const:
  652.     case tok_dp_const:
  653.   token->value.dbl = sign*token->value.dbl;
  654.   break;
  655.  }
  656.  token->class = tok_complex_const;
  657.  
  658.  while(iswhitespace(curr_char))
  659.    advance();
  660.  
  661.  
  662.  advance();  /* skip over the comma */
  663.  
  664.  while(iswhitespace(curr_char))
  665.       advance();
  666.  if(curr_char == '+' || curr_char == '-') {
  667.       if(curr_char == '-') sign = -1.0;
  668.       advance();
  669.       SKIP_SP;
  670.  }
  671. if(debug_lexer){
  672. fprintf(list_fd,"\n,");
  673. if(sign < 0.0) fprintf(list_fd," -");
  674. }
  675.  get_number(&imag_part);
  676.  
  677.  
  678.  while(iswhitespace(curr_char))
  679.     advance();
  680.  
  681.  advance(); /* skip over final paren */
  682.  
  683. if(debug_lexer)
  684. fprintf(list_fd,"\n)");
  685.  
  686. }
  687.  
  688. PRIVATE void
  689. get_punctuation(token)
  690.  Token *token;
  691. {
  692.  initial_flag = FALSE;
  693.  
  694.  if(curr_char == '*' && next_char == '*') {
  695.   token->class = tok_power;
  696.   advance();
  697.  }
  698.  else if(curr_char == '/' && next_char == '/' ) {
  699.   token->class = tok_concat;
  700.   advance();
  701.  }
  702.   /* paren can be the start of complex constant if everything
  703.      is just right. Maybe more tests needed here. */
  704.  else if(complex_const_allowed && curr_char == '(' &&
  705.       prev_token_class != tok_identifier
  706.       && looking_at(tok_complex_const)) {
  707.   get_complex_const(token);
  708.   return;
  709.  }
  710.  else
  711.   token->class = curr_char;
  712.  
  713.  
  714. if(debug_lexer) {
  715.  if(token->class == EOS)
  716.   fprintf(list_fd,"\n\t\t\tEOS");
  717.  else if(token->class == tok_power)
  718.   fprintf(list_fd,"\nPunctuation:\t\t**");
  719.  else if(token->class == tok_concat)
  720.   fprintf(list_fd,"\nPunctuation:\t\t//");
  721.  else
  722.   fprintf(list_fd,"\nPunctuation:\t\t%c",token->class);
  723.  }
  724.  advance();
  725. } /* get_punctuation */
  726.  
  727.  
  728.  
  729. PRIVATE void
  730. get_string(token)       /* Gets string of form 'aaaa' */
  731.  Token *token;
  732. {
  733.  int i,len;
  734.  
  735. /* String consts are not stored unless the macro name LEX_STORE_STRINGS
  736.    is defined. */
  737. #ifdef LEX_STORE_STRINGS
  738.  char *s;
  739.  char tmpstr[MAXSTR+1];
  740. #else
  741.  char *s = "Not stored";
  742. #endif
  743.  
  744.  initial_flag = FALSE;
  745.  inside_quotes = TRUE;
  746.  
  747.  advance();      /* Gobble leading quote */
  748.  i = len = 0;
  749.  for(;;) {
  750.   while(curr_char == EOL)
  751.    advance();
  752.   if(curr_char == EOS || curr_char == EOF) {
  753.    yyerror("Closing quote missing from string");
  754.    break;
  755.   }
  756.   if(curr_char == '\'') {
  757.    do {     /* Handle possible embedded EOL */
  758.     advance();
  759.    } while(curr_char == EOL);
  760.    if(curr_char == '\'') { /* '' becomes ' in string */
  761. #ifdef LEX_STORE_STRINGS
  762.     if(i < MAXSTR)
  763.      tmpstr[i++] = curr_char;
  764. #endif
  765.     ++len;
  766.     advance();
  767.    }
  768.    else {
  769.     break;  /* It was a closing quote after all */
  770.    }
  771.   }
  772.   else {
  773. #ifdef LEX_STORE_STRINGS
  774.    if(i < MAXSTR)
  775.     tmpstr[i++] = curr_char;
  776. #endif
  777.    ++len;
  778.    advance();
  779.   }
  780.  }
  781. #ifdef LEX_STORE_STRINGS
  782.  tmpstr[i++] = '\0';
  783.  if( (s=(char *)calloc((unsigned)i,1)) == (char *)NULL ) {
  784.   fprintf(stderr,"Out of space at line %u\n",line_num);
  785.  }
  786.  else {
  787.   (void) strcpy(s,tmpstr);
  788.  }
  789. #endif
  790.  if(len == 0) {
  791.   warning(line_num,col_num,
  792.    "Zero-length string not allowed\n");
  793.  }
  794.  
  795.  inside_quotes = FALSE;
  796.  
  797.  token->class = tok_string;
  798.  token->value.string = s;
  799.  if(debug_lexer)
  800.   fprintf(list_fd,"\nString:\t\t\t%s",s);
  801.  
  802. } /* get_string */
  803.  
  804.  
  805. /* End of Forlex module */
  806.  
  807. /*
  808. II. Advance
  809. */
  810.  
  811. /* advance.c:
  812.  
  813.  Low-level input routines for Fortran program checker.
  814.  
  815.  Shared functions defined:
  816.   init_scan() Initializes an input stream.
  817.   finish_scan() Finishes processing an input stream.
  818.   advance() Reads next char, removing comments and
  819.     handling continuation lines.
  820.   looking_at() Handles lookahead up to end of line.
  821.  
  822.   flush_line_out(n) Prints lines up to line n if not already
  823.     printed, so error messages come out looking OK.
  824. */
  825.  
  826.  
  827.  /* Define tab stops: nxttab[col_num] is column of next tab stop */
  828.  
  829. #define do8(X) X,X,X,X,X,X,X,X
  830. PRIVATE int nxttab[]={ 0, do8(9), do8(17), do8(25), do8(33),
  831.   do8(41), do8(49), do8(57), do8(65), do8(73), do8(81)};
  832.  
  833. PRIVATE int
  834.  next_index,  /* Index in line of next_char */
  835.  prev_comment_line, /* True if previous line was comment */
  836.  curr_comment_line, /* True if current line is comment */
  837.  noncomment_line_count, /* Number of noncomment lines read so far */
  838.  line_is_printed, /* True if line has been flushed (printed) */
  839.  prev_line_is_printed, /* True if line has been flushed (printed) */
  840.  sticky_EOF;  /* Signal to delay EOF a bit for sake
  841.        of error messages in include files. */
  842. PRIVATE unsigned
  843.  prev_line_num;  /* line number of previous input line */
  844.  
  845. unsigned prev_stmt_line_num; /* line number of previous noncomment */
  846.  
  847. PRIVATE char
  848.  lineA[MAXLINE+1],lineB[MAXLINE+1],  /* Buffers holding input lines */
  849.  *prev_line,*line;      /* Pointers to input buffers */
  850.  
  851. PRIVATE int
  852.  is_comment(), is_continuation(), is_overlength(), see_a_number();
  853. PRIVATE char
  854.  *getstrn();
  855.  
  856. #ifdef ALLOW_INCLUDE
  857. /* Definition of structure for saving the input stream parameters while
  858.    processing an include file.
  859. */
  860.  
  861. typedef struct {
  862.   FILE     *input_fd;
  863.   char    *fname;
  864.   char     line[MAXLINE];  /* MAXLINE is defined in forchek.h */
  865.   int      curr_char;
  866.   int      next_char;
  867.   int    next_index;
  868.   int    col_num;
  869.   int    next_col_num;
  870.   int    line_is_printed;
  871.   int    do_list;
  872.   unsigned line_num;
  873.   unsigned next_line_num;
  874. } IncludeFileStack;
  875.  
  876. PRIVATE IncludeFileStack include_stack[MAX_INCLUDE_DEPTH];
  877. #endif /*ALLOW_INCLUDE*/
  878.  
  879. PRIVATE void
  880.  init_stream();
  881. PRIVATE int
  882.  push_include_file(),pop_include_file();
  883.  
  884. #ifdef ALLOW_INCLUDE  /* defns of include-file handlers */
  885.  
  886. PRIVATE int
  887. push_include_file(fname,fd)
  888.  char *fname;
  889.  FILE *fd;
  890. {
  891.   if (incdepth == MAX_INCLUDE_DEPTH) {
  892.     yyerror("Oops! include files nested too deep");
  893.     return FALSE;
  894.   }
  895.  
  896. if(debug_latest){
  897. fprintf(list_fd,"\npush_include_file: curr_char=%c (%d)",curr_char,curr_char);
  898. }
  899.  
  900.   include_stack[incdepth].input_fd = input_fd;
  901.   input_fd = fd;
  902.  
  903.   include_stack[incdepth].fname = current_filename;
  904.   current_filename = fname;
  905.  
  906.   strcpy(include_stack[incdepth].line,line);
  907.   include_stack[incdepth].curr_char = curr_char;
  908.   include_stack[incdepth].next_char = next_char;
  909.   include_stack[incdepth].next_index = next_index;
  910.   include_stack[incdepth].col_num = col_num;
  911.   include_stack[incdepth].next_col_num = next_col_num;
  912.   include_stack[incdepth].line_is_printed = line_is_printed;
  913.   include_stack[incdepth].line_num = line_num;
  914.   include_stack[incdepth].next_line_num = next_line_num;
  915.   include_stack[incdepth].do_list = do_list;
  916.  
  917.   incdepth++;
  918.  
  919.   init_stream();
  920.  
  921.   return TRUE;
  922. }
  923.  
  924. PRIVATE int
  925. pop_include_file()
  926. {
  927. if(debug_latest){
  928. fprintf(list_fd,"\npop_include_file: line %u = %s depth %d",line_num,line,
  929. incdepth);
  930. }
  931.  
  932.   if (incdepth == 0) { /* Stack empty: no include file to pop. */
  933.     return FALSE;
  934.   }
  935.   incdepth--;
  936.  
  937.  
  938.   if(do_list) {
  939.     flush_line_out(next_line_num);
  940.     fprintf(list_fd,"\nResuming file %s:",
  941.      include_stack[incdepth].fname);
  942.   }
  943.  
  944.   fclose(input_fd);
  945.   input_fd = include_stack[incdepth].input_fd;
  946.  
  947.   current_filename = include_stack[incdepth].fname;
  948.  
  949.   strcpy(line,include_stack[incdepth].line);
  950.   curr_char = include_stack[incdepth].curr_char;
  951.   next_char = include_stack[incdepth].next_char;
  952.   next_index = include_stack[incdepth].next_index;
  953.   col_num = include_stack[incdepth].col_num;
  954.   next_col_num = include_stack[incdepth].next_col_num;
  955.   line_is_printed = include_stack[incdepth].line_is_printed;
  956.   line_num = include_stack[incdepth].line_num;
  957.   next_line_num = include_stack[incdepth].next_line_num;
  958.   do_list = include_stack[incdepth].do_list;
  959.  
  960.   curr_comment_line = FALSE;
  961.   prev_line_is_printed = TRUE;
  962.   initial_flag = TRUE;
  963.   sticky_EOF = TRUE;
  964.  
  965.   return TRUE;
  966. }
  967.  
  968.  
  969. void
  970. open_include_file(fname)
  971.      char *fname;
  972. {
  973.   FILE *fd;
  974. #ifdef VMS_INCLUDE
  975.   int list_option=FALSE; /* /[NO]LIST qualifier: default=NOLIST */
  976. #endif /*VMS_INCLUDE*/
  977.  
  978. #ifdef VMS_INCLUDE /* for VMS: default extension is .for */
  979.   if(has_extension(fname,"/nolist")) {
  980.     list_option = FALSE;
  981.     fname[strlen(fname)-strlen("/nolist")] = '\0'; /* trim off qualifier */
  982.   }
  983.   else if(has_extension(fname,"/list")) {
  984.     list_option = TRUE;
  985.     fname[strlen(fname)-strlen("/list")] = '\0'; /* trim off qualifier */
  986.   }
  987.   fname = add_ext(fname, DEF_SRC_EXTENSION);
  988. #endif
  989.  
  990.   if ((fd = fopen(fname,"r")) == NULL) {
  991.     fprintf(stderr,"\nerror opening include file %s\n",fname);
  992.     return;
  993.   }
  994.  
  995.    /* Print the INCLUDE line if do_list */
  996.   if(do_list)
  997.     flush_line_out(prev_line_num);
  998.  
  999.    /* Report inclusion of file */
  1000.   if(verbose || do_list)
  1001.     fprintf(list_fd,"\nIncluding file %s:",fname);
  1002.  
  1003.   /* Save the current input stream and then open
  1004.      the include file as input stream. */
  1005.   if( push_include_file(fname,fd) ) {
  1006. #ifdef VMS_INCLUDE
  1007.  /* put /[NO]LIST option into effect */
  1008.       if(do_list != list_option)
  1009.  fprintf(list_fd," (listing %s)", list_option? "on":"off");
  1010.       do_list = list_option;
  1011. #endif /*VMS_INCLUDE*/
  1012.   }
  1013.   else
  1014.     fclose(fd);
  1015. }
  1016.  
  1017. #else /* no ALLOW_INCLUDE */
  1018.     /* disabled forms of include handlers */
  1019. PRIVATE int
  1020. push_include_file(fname,fd)
  1021.  char *fname;
  1022.  FILE *fd;
  1023. {return FALSE;}
  1024.  
  1025. PRIVATE int
  1026. pop_include_file()
  1027. {return FALSE;}
  1028.  
  1029. void
  1030. open_include_file(fname)
  1031.      char *fname;
  1032. {}
  1033.  
  1034. #endif /*ALLOW_INCLUDE*/
  1035.  
  1036. void
  1037. init_scan()   /* Starts reading a file */
  1038. {
  1039.  tab_count = 0;
  1040.  incdepth = 0;
  1041.  
  1042.  line = lineA;  /* Start out reading into buffer A */
  1043.  prev_line = lineB;
  1044.  
  1045.  init_stream();
  1046. }
  1047.  
  1048. PRIVATE void
  1049. init_stream()  /* Initializes a new input stream */
  1050. {
  1051.  curr_comment_line = FALSE;
  1052.  inside_quotes = FALSE;
  1053.  line_is_printed = TRUE;
  1054.  prev_line_is_printed = TRUE;
  1055.  noncomment_line_count = 0;
  1056.  
  1057.  next_index = -1; /* Startup as if just read a blank line */
  1058.  next_char = EOS;
  1059.  curr_char = EOS;
  1060.  next_col_num = 0;
  1061.  next_line_num = 0;
  1062.  prev_line_num = prev_stmt_line_num = 0;
  1063.  sticky_EOF = TRUE;
  1064.  
  1065.  line[0] = '\0';
  1066.  advance();  /* put 1st two chars in the pipeline */
  1067.  advance();
  1068.  advance();  /* gobble the artificial initial EOS */
  1069. }
  1070.  
  1071.  
  1072. void
  1073. finish_scan()
  1074. {
  1075.   /* clean up if no END statement at EOF */
  1076.  check_seq_header((Token *)NULL);
  1077.   /* print last line if not already done */
  1078.  if(do_list)
  1079.      flush_line_out(line_num);
  1080. }
  1081.  
  1082. #ifdef INLINE_COMMENT_CHAR
  1083.  /* macro is used on next_char: must test curr_char to avoid
  1084.     being fooled by '!' without messing up on 'xxx'! either.
  1085.     Note that inside_quotes does not yet reflect curr_char. */
  1086. #define inline_comment(c) ((inside_quotes == (curr_char == '\'')) && \
  1087.       (c)==INLINE_COMMENT_CHAR)
  1088. #endif
  1089.  
  1090. void
  1091. advance()
  1092. {
  1093.     int eol_skip = FALSE;
  1094.     do{
  1095.  while(next_char == EOF) {   /* Stick at EOF */
  1096.   if(curr_char == EOS || curr_char == EOF) {
  1097.  
  1098.     /* Pause to allow parse actions at end of stmt
  1099.        to have correct file context before popping
  1100.        the include file.  Effect is to send an extra
  1101.        EOS to parser at end of file. */
  1102.     if(sticky_EOF) {
  1103.       sticky_EOF = FALSE;
  1104.       return;
  1105.     }
  1106.     /* At EOF: close include file if any,
  1107.        otherwise yield an EOF character. */
  1108.     if( ! pop_include_file() ) {
  1109.       curr_char = EOF;
  1110.       return;
  1111.     }
  1112.   }
  1113.   else {
  1114.     curr_char = EOS;
  1115.     return;
  1116.   }
  1117.  }
  1118.  
  1119.  if(curr_char == EOS)
  1120.   initial_flag = TRUE;
  1121.  
  1122.  if(! eol_skip) {
  1123.      curr_char = next_char;   /* Step to next char of input */
  1124.      col_num = next_col_num;
  1125.      line_num = next_line_num;
  1126.  }
  1127.  
  1128.  if(next_char == '\t'){    /* Handle tabs in input */
  1129.  
  1130.   next_col_num = nxttab[next_col_num];
  1131.  
  1132.   if( ! inside_quotes )
  1133.       tab_count++; /*  for portability warning */
  1134.  }
  1135.  else {
  1136.   next_col_num++;
  1137.  }
  1138.  
  1139.  next_char = line[++next_index];
  1140.  
  1141.    /* If end of line is reached, input a new line.
  1142.     */
  1143.  while(next_col_num > max_stmt_col || next_char == '\0'
  1144. #ifdef INLINE_COMMENT_CHAR
  1145.  || inline_comment(next_char)
  1146. #endif
  1147.  ){
  1148.   do{
  1149.    if(do_list) /* print prev line if not printed yet */
  1150.      flush_line_out(prev_line_num);
  1151.  
  1152.    if( f77_standard ) {
  1153.      if( !prev_comment_line && is_overlength(prev_line)){
  1154.          nonstandard(prev_line_num,73);
  1155.          msg_tail(": characters past 72 columns");
  1156.      }
  1157. #ifdef INLINE_COMMENT_CHAR
  1158.      if( !curr_comment_line && inline_comment(next_char)){
  1159.          nonstandard(next_line_num,next_col_num);
  1160.          msg_tail(": inline comment");
  1161.      }
  1162. #endif
  1163.           }
  1164.        /* Swap input buffers to get ready for new line.
  1165.           But throw away comment lines if do_list is
  1166.           false, so error messages will work right.
  1167.         */
  1168.    if(do_list || ! curr_comment_line) {
  1169.        char *temp=line;
  1170.        line = prev_line;
  1171.        prev_line=temp;
  1172.        if(! curr_comment_line)
  1173.          prev_stmt_line_num = line_num;
  1174.        prev_line_num = next_line_num;
  1175.        prev_line_is_printed = line_is_printed;
  1176.    }
  1177.  
  1178.    ++next_line_num;
  1179.    line_is_printed = FALSE;
  1180.    if( getstrn(line,MAXLINE+1,input_fd) == NULL ) {
  1181.     next_char = EOF;
  1182.     line_is_printed = TRUE;
  1183.     return;
  1184.    }
  1185.  
  1186.    /*  Keep track of prior-comment-line situation */
  1187.    prev_comment_line = curr_comment_line;
  1188.  
  1189.   } while( (curr_comment_line = is_comment(line)) != FALSE);
  1190.   ++noncomment_line_count;
  1191.  
  1192.    /* Handle continuation lines */
  1193.   if( (next_index = is_continuation(line)) != 0) {
  1194.     /* It is a continuation */
  1195.       if(eol_is_space) {
  1196.    next_char = EOL;
  1197.    next_col_num = 6;
  1198.       }
  1199.       else {
  1200.    next_char = line[++next_index];
  1201.    next_col_num = 7;
  1202.    eol_skip = TRUE; /* skip continued leading space */
  1203.       }
  1204.     /* Issue warnings if contin in funny places */
  1205.    if(noncomment_line_count == 1)
  1206.        warning(next_line_num,6,
  1207.       "Continuation mark found in first statement of file");
  1208.    if( prev_comment_line )
  1209.        warning(next_line_num,6,
  1210.       "Continuation follows comment or blank line");
  1211.   }
  1212.   else {
  1213.     /* It is not a continuation */
  1214.       next_char = EOS;
  1215.       next_col_num = 0;
  1216.       next_index = -1;
  1217.   }
  1218.  }/*end while( end of line reached )*/
  1219.  
  1220.   /* Avoid letting a '0' in column 6 become a token */
  1221.  if(next_col_num == 6 && next_char == '0')
  1222.   next_char = ' ';
  1223.  
  1224.    /* elide EOL and following space of continued
  1225.       stmts if requested */
  1226.  eol_skip = (eol_skip && isspace(next_char));
  1227.  
  1228.    }while(eol_skip);/*end do*/
  1229.  
  1230. }/* end advance */
  1231.  
  1232.  
  1233.  /*  Function which returns 0 if line is not a comment, 1 if it is.
  1234.   *  Comment is ANSI standard: C or c or * in column 1, or blank line.
  1235.   */
  1236.  
  1237. PRIVATE int
  1238. is_comment(s)
  1239.  char s[];
  1240. {
  1241.  int i,c= makeupper(s[0]);
  1242.  if( c == 'C' || c == '*' )
  1243.   return TRUE;
  1244.  
  1245.  for(i=0; s[i] != '\0'; i++)
  1246.   if( !isspace(s[i]))
  1247. #ifdef INLINE_COMMENT_CHAR
  1248.     if(s[i]==INLINE_COMMENT_CHAR) {
  1249.         if(f77_standard) {
  1250.      int j,col;
  1251.      for(j=0,col=1; j<i; j++) /* compute col num */
  1252.        if(s[j] == '\t') col = nxttab[col];
  1253.        else      col++;
  1254.      nonstandard(next_line_num,col);
  1255.      msg_tail(": inline comment");
  1256.         }
  1257.         return TRUE;
  1258.      }
  1259.      else
  1260.          return FALSE;
  1261. #else
  1262.    return FALSE;
  1263. #endif
  1264.  return TRUE;  /* blank line */
  1265. }
  1266.  
  1267.  
  1268.  /*  Function which returns 0 if line is a not continuation line.
  1269.   *  If line is a continuation, returns index in line of
  1270.   *  the continuation mark.
  1271.   */
  1272. PRIVATE int
  1273. is_continuation(s)
  1274.  char s[];
  1275. {
  1276.  int col,i,c;
  1277.     /* skip to col 6 */
  1278.  for(i=0,col=1; col < 6 && s[i] != '\0'; i++) {
  1279.   if(s[i] == '\t')
  1280.    col = nxttab[col];
  1281.   else
  1282.    col++;
  1283.  }
  1284.  c = s[i];
  1285.  
  1286.  if ( col == 6 && c != '\0' && !isspace(c) && c != '0')
  1287.   return i;
  1288.  else
  1289.   return 0;
  1290.  
  1291. }
  1292.  
  1293. int
  1294. flush_line_out(n) /* Prints lines up to line #n if not yet printed */
  1295.     unsigned n;  /* Returns TRUE if line was printed, else FALSE */
  1296. {
  1297.    /* Print previous line only if do_list TRUE */
  1298.  if( !prev_line_is_printed
  1299.   && ((n == prev_line_num) || (n > prev_line_num && do_list)) ) {
  1300.     print_a_line(list_fd,prev_line,prev_line_num);
  1301.     prev_line_is_printed = TRUE;
  1302.  }
  1303.  if(n >= next_line_num && !line_is_printed) {
  1304.     print_a_line(list_fd,line,next_line_num);
  1305.     line_is_printed = TRUE;
  1306.  }
  1307.     return ( do_list ||
  1308.       (prev_line_is_printed && n == prev_line_num) ||
  1309.             (line_is_printed && n == next_line_num) );
  1310. }
  1311.  
  1312.  
  1313.  /*  Function to read n-1 characters, or up to newline, whichever
  1314.   *  comes first.  Differs from fgets in that the newline is replaced
  1315.   *  by null, and characters up to newline (if any) past the n-1st
  1316.   *  are read and thrown away.
  1317.   *  Returns NULL when end-of-file or error is encountered.
  1318.   */
  1319. PRIVATE char *
  1320. getstrn(s,n,fd)
  1321.  char s[];
  1322.  int n;
  1323.  FILE *fd;
  1324. {
  1325.  int i=0,c;
  1326.  while( (c=getc(fd)) != '\n' ) {
  1327.   if(c == EOF)
  1328.    return NULL;
  1329.  
  1330.   if(i < n-1)
  1331.    s[i++] = c;
  1332.  }
  1333.  s[i] = '\0';
  1334.  return s;
  1335. }
  1336.  
  1337.  
  1338.  /* Function which looks ahead as far as end of line to see if input
  1339.     cursor is sitting at start of a token of the given class. */
  1340.  /* N.B. right now only looks for edit descriptor or relop
  1341.     or complex constant */
  1342. int
  1343. looking_at(token_class)
  1344.  int token_class;
  1345. {
  1346.     int index;
  1347.  
  1348.     if( eol_is_space && line_num != next_line_num )
  1349.  return FALSE; /* Looking at next line already */
  1350.  
  1351.     switch(token_class) {
  1352.  
  1353. #if 0/* This case is no longer used */
  1354.       case tok_edit_descriptor:
  1355.         if( ! inside_format ) /* Gotta be inside a format spec */
  1356.   return FALSE;
  1357.  
  1358.  index = next_index; /* Move past the E or D */
  1359.  
  1360.  if( ! isdigit(line[index++]) )
  1361.   return FALSE;  /* Must start with w = integer */
  1362.  while( isdigit(line[index]) ) {
  1363.   ++index;  /* Scan over the w part */
  1364.  }
  1365.  
  1366.  if( line[index++] != '.' )
  1367.   return FALSE;  /* Now must have decimal point */
  1368.  
  1369.  if( ! isdigit(line[index++]) )
  1370.   return FALSE;  /* Must now have d = integer */
  1371.  
  1372.  break;
  1373. #endif
  1374.       case tok_relop:  /* called with curr_char == '.' */
  1375.  
  1376.  if( !isaletter( line[next_index] ) ) /* next char must be letter */
  1377.   return FALSE;
  1378.  
  1379.  if( makeupper( line[next_index] ) == 'D' ) /* D.P. exponent */
  1380.   return FALSE;
  1381.  
  1382.    /* if next char is any other letter but 'E', cannot be
  1383.        exponent.  If 'E', must be EQ to be relop */
  1384.  if( makeupper( line[next_index] ) == 'E'
  1385.   && makeupper( line[next_index+1] ) != 'Q' )
  1386.   return FALSE;
  1387.  
  1388.  break;
  1389.  
  1390.       case tok_complex_const:
  1391.  index = next_index;
  1392.  
  1393.  if( (index = see_a_number(line,index)) < 0 )
  1394.    return FALSE;
  1395.  while(line[index] != '\0' && isspace(line[index]))
  1396.    index++;
  1397.  
  1398.  if( line[index] != ',' )
  1399.    return FALSE;
  1400.  ++index;
  1401.  
  1402.  if( (index = see_a_number(line,index)) < 0 )
  1403.    return FALSE;
  1404.  while(line[index] != '\0' && isspace(line[index]))
  1405.    index++;
  1406.  
  1407.  if(line[index] != ')')
  1408.    return FALSE;
  1409.  
  1410.  break;
  1411.  
  1412.       default:
  1413.  return FALSE;
  1414.     }
  1415.  
  1416.     return TRUE; /* passed all the tests */
  1417.  
  1418. }
  1419.  
  1420.  /* see_a_number returns -1 if there is no valid numeric constant
  1421.     in string s starting at index i.  If valid number found, it
  1422.     returns the index of the next character after the constant.
  1423.     Leading whitespace in s is skipped.*/
  1424.  
  1425. #ifdef BLANKS_IN_NUMBERS
  1426. #define SKIP_SPACE    while(s[i] != '\0' && isspace(s[i])) i++
  1427. #else
  1428. #define SKIP_SPACE
  1429. #endif
  1430.  
  1431. PRIVATE int
  1432. see_a_number(s,i)
  1433.    char s[];
  1434.    int i;
  1435. {
  1436.    int isave = i,j;
  1437.    int digit_seen = FALSE;
  1438.  
  1439.    while(s[i] != '\0' && isspace(s[i]))
  1440.      i++;
  1441.    /* move past optional preceding sign */
  1442.    if(s[i] == '-' || s[i] == '+' ) {
  1443.      i++;
  1444.      SKIP_SPACE;
  1445.    }
  1446.  
  1447.   /* move past ddd or ddd. or .ddd or ddd.ddd */
  1448.    if(isdigit(s[i]))
  1449.      digit_seen = TRUE;
  1450.    while(isdigit(s[i])) {
  1451.      i++;
  1452.      SKIP_SPACE;
  1453.    }
  1454.    if(s[i] == '.') {
  1455.      i++;
  1456.      SKIP_SPACE;
  1457.      if(isdigit(s[i]))
  1458.        digit_seen = TRUE;
  1459.      while(isdigit(s[i])) {
  1460.        i++;
  1461.        SKIP_SPACE;
  1462.      }
  1463.    }
  1464.  
  1465.   /* no digits seen: bail out now */
  1466.    if(! digit_seen)
  1467.      return -1;
  1468.  
  1469.   /* look for exponential part.  The standard does not
  1470.      allow D, but we will, just in case. */
  1471.    if(makeupper(s[i]) == 'E' || makeupper(s[i]) == 'D') {
  1472.      i++;
  1473.      if(s[i] == '+' || s[i] == '-')
  1474.        i++;
  1475.      if(!isdigit(s[i]))
  1476.        return -1;
  1477.      while(isdigit(s[i]))
  1478.        i++;
  1479.    }
  1480.  
  1481. if(debug_latest) {fprintf(list_fd,"\nsee_a_number: ");
  1482. for(j=isave; j<i; j++) printf("%c",s[j]);}
  1483.  
  1484.    return i;
  1485. }
  1486.  
  1487. PRIVATE
  1488. int
  1489. is_overlength(s) /* checks line for having nonblanks past col 72 */
  1490.  char *s;
  1491. {
  1492.  int i,col;
  1493.  for(col=1,i=0; s[i] != '\0'; i++) {
  1494.  
  1495.      if(col > 72 && !isspace(s[i]))
  1496.   return TRUE;
  1497.  
  1498.    /* Count columns taking tabs into consideration */
  1499.      if(s[i] == '\t')
  1500.   col = nxttab[col];
  1501.      else
  1502.   ++col;
  1503.  }
  1504.  return FALSE;
  1505. }
  1506.  
  1507. /* End of module Advance */
  1508.  
  1509. /*
  1510.  
  1511. III. Keywords
  1512.  
  1513. */
  1514.  
  1515. /*  keywords.c:
  1516.  Determines (to the best of its current ability) whether a given
  1517.  identifier is a keyword or not.
  1518.  
  1519.  Keywords may be used as variable names subject to the following
  1520.  limitations (see forchek.doc for explicit list):
  1521.  
  1522.   Use freely:
  1523.  
  1524.    any keyword with IK | NP flags
  1525.    any keyword with TY flag (data type names)
  1526.    FUNCTION
  1527.    TO
  1528.  
  1529.   Use as scalar variables only (not array, and not char
  1530.   if substring referenced):
  1531.  
  1532.    any keyword with IK flag
  1533.  
  1534.   Reserved:
  1535.  
  1536.    all others  (this is now the empty set)
  1537.  
  1538. */
  1539.  
  1540.  
  1541. #define IK 01 /* initial keyword of a statement */
  1542. #define NP 02 /* not followed by ( or = if initial */
  1543. #define MP 04 /* must be followed by ( */
  1544. #define NI 010 /* disallowed in logical IF */
  1545. #define EK 020 /* cannot be followed by another keyword */
  1546. #define TY 040 /* data type name */
  1547. #define EMPTY 256
  1548.  
  1549. struct {
  1550.  char *name;
  1551.  int class,
  1552.  context;
  1553. } keywords[]={
  1554. {"ASSIGN", tok_ASSIGN, IK | NP | EK},
  1555. {"ACCEPT", tok_ACCEPT, IK | EK},
  1556. {"BACKSPACE", tok_BACKSPACE, IK | EK},
  1557. {"BLOCK", tok_BLOCK, IK | NP | NI},
  1558. {"CALL", tok_CALL, IK | NP | EK},
  1559. {"CHARACTER", tok_CHARACTER, IK | NI | EK | TY},
  1560. {"CLOSE", tok_CLOSE, IK | EK | MP},
  1561. {"COMMON", tok_COMMON, IK | NP | NI | EK},
  1562. {"COMPLEX", tok_COMPLEX, IK | NI | EK | TY},
  1563. {"CONTINUE", tok_CONTINUE, IK | NP | EK},
  1564. {"DATA", tok_DATA, IK | NI | EK},
  1565. {"DIMENSION", tok_DIMENSION, IK | NP | NI | EK},
  1566. {"DO",  tok_DO,  IK | NP | NI},
  1567. {"DOUBLE", tok_DOUBLE, IK | NP | NI},
  1568. {"DOWHILE", tok_DOWHILE, IK | NI | EK},
  1569. {"ELSE", tok_ELSE, IK | NP | NI},
  1570. {"ELSEIF", tok_ELSEIF, IK | NI | EK},
  1571. {"END",  tok_END, IK | NP | NI},
  1572. {"ENDDO", tok_ENDDO, IK | NP | NI | EK},
  1573. {"ENDFILE", tok_ENDFILE, IK | EK},
  1574. {"ENDIF", tok_ENDIF, IK | NP | NI | EK},
  1575. {"ENTRY", tok_ENTRY, IK | NP | NI | EK},
  1576. {"EQUIVALENCE", tok_EQUIVALENCE,IK | NI | EK | MP},
  1577. {"EXTERNAL", tok_EXTERNAL, IK | NP | NI | EK},
  1578. {"FORMAT", tok_FORMAT, IK | NI | EK | MP},
  1579. {"FUNCTION", tok_FUNCTION, NP | NI | EK},
  1580. {"GOTO", tok_GOTO, IK | EK},
  1581. {"GO",  tok_GO,  IK | NP},
  1582. {"IF",  tok_IF,  IK | NI | EK},
  1583. {"IMPLICIT", tok_IMPLICIT, IK | NP | NI},
  1584. {"INCLUDE", tok_INCLUDE, IK | NP | NI | EK},
  1585. {"INQUIRE", tok_INQUIRE, IK | EK},
  1586. {"INTEGER", tok_INTEGER, IK | NI | EK | TY},
  1587. {"INTRINSIC", tok_INTRINSIC, IK | NP | NI | EK},
  1588. {"LOGICAL", tok_LOGICAL, IK | NI | EK | TY},
  1589. {"OPEN", tok_OPEN, IK | EK | MP},
  1590. {"PARAMETER", tok_PARAMETER, IK | NI | EK | MP},
  1591. {"PAUSE", tok_PAUSE, IK | NP | EK},
  1592. {"PRECISION", tok_PRECISION, IK | NI | EK | TY},
  1593. {"PRINT", tok_PRINT, IK | EK},
  1594. {"PROGRAM", tok_PROGRAM, IK | NP | NI | EK},
  1595. {"READ", tok_READ, IK | EK},
  1596. {"REAL", tok_REAL, IK | NI | EK | TY},
  1597. {"RETURN", tok_RETURN, IK | EK},
  1598. {"REWIND", tok_REWIND, IK | EK},
  1599. {"SAVE", tok_SAVE, IK | NP | NI | EK},
  1600. {"STOP", tok_STOP, IK | NP | EK},
  1601. {"SUBROUTINE", tok_SUBROUTINE, IK | NP | NI | EK},
  1602. {"TO",  tok_TO,  NI | EK},
  1603. {"THEN", tok_THEN, IK | NP | EK},
  1604. {"TYPE", tok_TYPE, IK | EK},
  1605. {"WHILE", tok_WHILE, IK | NI | EK},
  1606. {"WRITE", tok_WRITE, IK | EK | MP},
  1607. {NULL,0,0},
  1608. };
  1609.  
  1610.   /* Macro to test if all the specified bits are set */
  1611. #define MATCH(Context) ((keywords[i].context & (Context)) == (Context))
  1612.  
  1613.  
  1614.  /* Returns keyword token class or 0 if not a keyword.  This
  1615.     version is able to handle those keywords which can only occur
  1616.     at the start of a statement and are never followed by ( or =
  1617.     so that they can be used as variables.
  1618.   */
  1619.  
  1620. #ifdef KEYHASHSZ
  1621. int keyhashtab[KEYHASHSZ];
  1622. #else
  1623. int keyhashtab[1000];
  1624. #endif
  1625.  
  1626. /* Start of is_keyword */
  1627. int
  1628. is_keyword(s)
  1629.     char *s;
  1630. {
  1631.     unsigned h = kwd_hash(s) % KEYHASHSZ,
  1632.       ans = FALSE,
  1633.       i = keyhashtab[h];
  1634.     if( i != EMPTY && strcmp(keywords[i].name,s) == 0) {
  1635.         while(iswhitespace(curr_char))       /* Move to lookahead char */
  1636.       advance();
  1637.  
  1638.     if(debug_lexer){
  1639.  fprintf(list_fd,
  1640.   "\nkeyword %s: initialflag=%d ",keywords[i].name,initial_flag);
  1641.  fprintf(list_fd,
  1642.   "context=%o, next char=%c %o",keywords[i].context,
  1643.       curr_char,curr_char);
  1644.     }
  1645.  
  1646.      if( !initial_flag && MATCH(IK) ) {
  1647.    /* Dispose of names which can only occur in initial
  1648.       part of statement, if found elsewhere. */
  1649.       ans = FALSE;
  1650.      }
  1651.  
  1652.      else if( MATCH(IK|NP) ) {
  1653.    /* Here we disambiguate keywords found in initial
  1654.       part of statement: those which can only occur in
  1655.           initial position and never followed by '(' or '='
  1656.     */
  1657.   if( (curr_char != '(') && (curr_char != '=') ) {
  1658.    ans = TRUE;
  1659.   }
  1660.   else {
  1661.    ans = FALSE;
  1662.     }
  1663.      }
  1664.  
  1665.      else if( MATCH(TY) ){
  1666.    /* Handle data type names. */
  1667.  
  1668.   if(keywords[i].class == tok_PRECISION)
  1669.   {
  1670.       ans = (prev_token_class == tok_DOUBLE);
  1671.   }
  1672.   else
  1673.   {
  1674.       if( implicit_flag )
  1675.    ans = TRUE;
  1676.       else
  1677.    ans = (initial_flag &&
  1678.       (curr_char != '(') && (curr_char != '=') );
  1679.   }
  1680.      }
  1681.  
  1682.      else if(keywords[i].class == tok_FUNCTION) {
  1683.    /*  FUNCTION is handled as a special case.  It must
  1684.        always be followed by a letter (variable never can)
  1685.     */
  1686.   ans = (isaletter(curr_char));
  1687.      }
  1688.  
  1689.      else if(keywords[i].class == tok_TO) {
  1690.    /* TO is another special case.  Either must follow
  1691.       GO recognized previously or be followed by a
  1692.       variable name (in ASSIGN statement).
  1693.     */
  1694.       if(prev_token_class == tok_GO)
  1695.       ans = TRUE;
  1696.   else
  1697.       ans = ( isaletter(curr_char) );
  1698.      }
  1699.  
  1700.      else if( MATCH(IK) ) {
  1701.    /*  Handle keywords which must be in initial position,
  1702.        when found in initial position.  For the present,
  1703.        these are semi-reserved: if used for variables,
  1704.        must be scalar variables.  Then if used as variable
  1705.        must be followed by '='
  1706.     */
  1707.   ans = ( curr_char != '=' );
  1708.      }
  1709.      else{
  1710.      /* For now, other keywords are reserved. */
  1711.   ans = TRUE;
  1712.      }
  1713.  
  1714.      }  /* end if(strcmp...) */
  1715.  
  1716.  
  1717.    /* Save initial token class for use by parser.
  1718.       Either set it to keyword token or to id for
  1719.       assignment stmt. */
  1720.      if(initial_flag) {
  1721.  curr_stmt_class = (ans? keywords[i].class: tok_identifier);
  1722.      }
  1723.  
  1724.   /* Turn off the initial-keyword flag if this is a
  1725.      keyword that cannot be followed by another keyword
  1726.      or if it is not a keyword.
  1727.   */
  1728.     if(ans) {
  1729.   if(keywords[i].context & EK)
  1730.    initial_flag = FALSE;
  1731.   return keywords[i].class;
  1732.     }
  1733.     else {
  1734.   initial_flag = FALSE;
  1735.   return 0; /* Not found in list */
  1736.     }
  1737. }
  1738. /* End of is_keyword */
  1739.  
  1740.  
  1741.  
  1742. /*    init_keyhashtab.c:
  1743.                  Initializes the keyword hash table by clearing it to EMPTY
  1744.                  and then hashes all the keywords into the table.
  1745. */
  1746.  
  1747.  
  1748. void
  1749. init_keyhashtab()
  1750. {
  1751.     unsigned i,h;
  1752.  
  1753.     for(i=0;i<KEYHASHSZ;i++) {
  1754.            keyhashtab[i] = EMPTY;
  1755.     }
  1756.     for(i=0; keywords[i].name != NULL; i++) {
  1757.     h = kwd_hash(keywords[i].name) % KEYHASHSZ;
  1758.     if( keyhashtab[h] == EMPTY ) {
  1759.   keyhashtab[h] = i;
  1760.            }
  1761.     else   { /* If there is a clash, there is a bug */
  1762. #ifdef KEYHASHSZ
  1763.   fprintf(stderr,"Oops-- Keyword hash clash at %s, %s\n",
  1764.    keywords[i].name,
  1765.    keywords[keyhashtab[h]].name);
  1766.   exit(1);
  1767. #else
  1768.   ++numclashes; /* for use in finding right key hash size */
  1769. #endif
  1770.     }
  1771.     }
  1772. }
  1773.